  /**
 * SafeCast logging application
 
 * by Robin Scheibler, 2011
 * safecast.org
 */

import processing.serial.*; // Serial library to read GPS and Geiger
import controlP5.*;         // GUI library to have nice interface

ControlP5 controlP5;  // Controller for GUI

DropdownList gpsMenu, geigerMenu; // DropDown lists for GPS, Geiger
DropdownList gpsDevMenu;  // DropDown to choose GPS device name
DropdownList measureMenu; // DropDown to choose measurement type (air, ...)
Textfield logfileTextField; // logfile name
Textarea statusDisplay; // display status messages
Textarea nicePanel; // display status messages

// Serial port objects
Serial gpsPort;    // Create object from Serial class for the GPS port
Serial geigerPort; // Create object from Serial class for the Geiber port

// GPS device and measurement type
int GPSSerialId    = -1; // GPS Serial Id
int GeigerSerialId = -1; // Geiger Serial Id
int gpsDevId       = -1; // GPS device id
int measureId      = -1; // Measurement type id (air, water, ...)

// Logging File stuff
String logfilename; // log file name
PrintWriter logFile;

// Lists with the types of different stuff
String[] gpsDeviceTypes = { "Canmore GT-730FL-S", "Garmin XXX" };
String[] measureTypes = { "Air", "Water", "Food" };

// State variables
boolean logging = false;

// Geiger related variable
String CPM;        // Counts per minutes from Geiger counter

// GPA related variables
String gpsDataArray[];
String data;
Float Latitude1 = 0.0;
String Latitude;
String NorSindicator;
Float Longitude1 = 0.0;
String Longitude;
String EorWindicator;
String Satellites;
String Precision;
String  Altitude;
String gpsdata;
String time;
int Quality;
String[] QualityDesc = {
    "0 - fix not available",
    "1 - GPS fix",
    "2 - Differential GPS fix",
    "3 = PPS fix",
    "4 = Real Time Kinematic",
    "5 = Float RTK",
    "6 = estimated (dead reckoning)",
    "7 = Manual input mode",
    "8 = Simulation mode"
};


// Start Setup Yeah
void setup() {
  size(600,400);
  PFont myFont = createFont(PFont.list()[2], 10); // font type
  textFont(myFont);
  frameRate(30);
  controlP5 = new ControlP5(this);

  // set tentative file name
  logfilename = "safecast-" + year() + month() + day()
                + "-" + hour() + minute() + second() + ".log";

  // Create Textfield for log file name
  logfileTextField = controlP5.addTextfield("logfilenamett",10,400-40,400,20);
  logfileTextField.setFocus(true);
  logfileTextField.setLabel("Log file name");
  logfileTextField.setText(logfilename);
  // Start Button!
  controlP5.addButton("start", 0, 600-120, 400-40, 50, 20);
  controlP5.addButton("stop", 0, 600-60, 400-40, 50, 20);
  // Status Panel
  statusDisplay = controlP5.addTextarea("statusDisplay",
    "Welcome to SafeCastLogging!\n",
    10, 200, 580, 150);
  statusDisplay.setColorBackground(color(60));
  // Info display Panel
  nicePanel = controlP5.addTextarea("nicePanel",
    "GEIGER+GPS DATA\n",
    10, 40, 300, 130);
  // Create DD menu for GPS Device Type
  gpsDevMenu = controlP5.addDropdownList("gpsDevMenu", 10,30,100,120);
  createDropDown(gpsDevMenu, "GPS Device", gpsDeviceTypes);
  // Create DD menu for GPS Serial Device
  gpsMenu = controlP5.addDropdownList("gpsMenu",120,30,160,120);
  createDropDown(gpsMenu, "GPS Serial Device", Serial.list());
  // Create DD menu for Measurement types
  measureMenu = controlP5.addDropdownList("measureMenu",290,30,100,120);
  createDropDown(measureMenu, "Measurement Type", measureTypes);
  // Create DD menu for Geiger Serial Device
  geigerMenu = controlP5.addDropdownList("geigerMenu",400,30,160,120);
  createDropDown(geigerMenu, "Geiger Serial Device", Serial.list());
}

void createDropDown(DropdownList ddl, String name, String[] items) {
  ddl.setBackgroundColor(color(190));
  ddl.setItemHeight(20);
  ddl.setBarHeight(15);
  ddl.captionLabel().set(name);
  ddl.captionLabel().style().marginTop = 3;
  ddl.captionLabel().style().marginLeft = 3;
  ddl.valueLabel().style().marginTop = 3;
  for(int i=0 ; i < items.length ; i++) {
    ddl.addItem(items[i], i);
  }
  ddl.setColorBackground(color(60));
  ddl.setColorActive(color(255,128));
}

void controlEvent(ControlEvent theEvent) {
  // PulldownMenu is if type ControlGroup.
  // A controlEvent will be triggered from within the ControlGroup.
  // therefore you need to check the originator of the Event with
  // if (theEvent.isGroup())
  // to avoid an error message from controlP5.

  if (theEvent.isGroup()) {
    // check if the Event was triggered from a ControlGroup
    println("Hi from Group.");
    println(theEvent.group().value()+" from "+theEvent.group());
    String EName = theEvent.name();
    if (EName.equals("gpsDevMenu"))
    {
      gpsDevId = int(theEvent.group().value());
    }
    else if (EName.equals("gpsMenu"))
    {
      GPSSerialId = int(theEvent.group().value());
    }
    else if (EName.equals("measureMenu"))
    {
      measureId = int(theEvent.group().value());
    }
    else if (EName.equals("geigerMenu"))
    {
      GeigerSerialId = int(theEvent.group().value());
    }
    else if (EName.equals("logfilenamett"))
    {
      logfilename = logfileTextField.getText();
      println(logfilename);
      logfileTextField.setText(logfilename);
    }
    else if (EName.equals("statusDisplay"))
    { }
  } else if(theEvent.isController()) {
    println("Hi from Controller.");
    println(theEvent.controller().value()+" from "+theEvent.controller());
    String EName = theEvent.controller().name();
    if (EName.equals("logfilenamett"))
    {
      logfilename = theEvent.controller().stringValue();
      logfileTextField.setText(logfilename);
      postStatusMessageln("Filename set to " + logfilename);
    } 
    else if (EName.equals("start"))
    { 
      if (logging == false)
        startLogging();
    }
    else if (EName.equals("stop"))
    { 
      if (logging == true)
        stopLogging();
    }
  }
}

void initSerial()
{
  gpsPort = new Serial(this, Serial.list()[GPSSerialId], 38400);
  geigerPort = new Serial(this, Serial.list()[GeigerSerialId], 9600);
}

void stopSerial()
{
  delay(1000);
  gpsPort.clear();
  gpsPort.stop();
  geigerPort.clear();
  geigerPort.stop();
}

void openLogFile()
{
  logfilename = logfileTextField.getText();
  if (logfilename.equals(""))
  {
    // set tentative file name
    logfilename = "safecast-" + year() + month() + day()
      + "-" + hour() + minute() + second() + ".log";
  }
  logFile = createWriter(logfilename);
}

void closeLogFile()
{
  logFile.flush();
  logFile.close();
  logfilename = "";
  logfileTextField.setText("");
}

void writeLog()
{
  String log_entry =
    year() + "-" + month() + "-" + day() + "," +
    hour() + ":" + minute() + ":" + second() + "," +
    CPM + "," +
    Latitude + "," + NorSindicator + "," +
    Longitude + "," + EorWindicator + "," +
    Quality + "," + Satellites + "," +
    Precision + "," + Altitude + "," +
    gpsDeviceTypes[gpsDevId] + "," + measureTypes[measureId] + "\n";

  logFile.print(log_entry);
  postStatusMessage(log_entry);
  logFile.flush();
}

int checkParam()
{
  // assume everything is alright
  int status = 1;
  // check
  if (gpsDevId == -1)
  {
    postStatusMessageln("*** GPS Device Name not set.");
    status = 0;
  }
  if (measureId == -1)
  {
    postStatusMessageln("*** Measurement time not set.");
    status = 0;
  }
  if (GPSSerialId == -1)
  {
    postStatusMessageln("*** GPS Serial Port not set.");
    status = 0;
  }
  if (GeigerSerialId == -1)
  {
    postStatusMessageln("*** Geiger Serial Port not set.");
    status = 0;
  }
  return status;
}

void postStatusMessage(String msg)
{
  String text = statusDisplay.text();
  text = text + msg;
  String[] lines = split(text, '\n');
  if (lines.length > 50)
  {
    text = lines[lines.length-50] + "\n";
    for (int i=lines.length-49 ; i < lines.length ; i++)
      text = text + lines[i] + "\n";
  }
  statusDisplay.setText(text);
}

void postStatusMessageln(String msg)
{
  postStatusMessage(msg + "\n");
}

void startLogging()
{
  // first check parameters
  if (checkParam() == 0)
  {
    postStatusMessageln("*** Couldn't start logging...");
    return;
  }
  // open file
  openLogFile();
  // open serial ports
  initSerial();
  // start running
  logging = true;
  postStatusMessageln("Started logging to " + logfilename);
}

void stopLogging()
{
  logging = false;
  closeLogFile();
  stopSerial();
  postStatusMessageln("Stopped Logging.");
}

void displayInfo()
{
  String line0 = "GEIGER+GPS DATA\n\n";
  String line1 = "CPM: " + CPM + "\n";
  String line2 = "Time: " + day() + ":" + minute() + ":" + second() + "\n";
  String line3 = "Latitude: " + Latitude1 + NorSindicator
      + ", Longitude: " + Longitude1 + EorWindicator + "\n";
  String line4 = "Altitude: " + Altitude + "\n";
  String line5 = 
      "Precision: " + Precision + ", Quality: " + QualityDesc[Quality]
      + ", Satellites: " + Satellites + "\n";
      
  nicePanel.setText(line0 + line1 + line2 + line3 + line4 + line5);
}

void draw() {
  background(128);

  if (logging)
  {
    if ( geigerPort.available() > 0) {  // If data is available,
      String cpm_from_srl
          = geigerPort.readStringUntil('\n');  // read it and store it in CPM
      if (cpm_from_srl != null)
      {
        CPM = cpm_from_srl.substring(0,cpm_from_srl.length()-2);
        gpsPort.clear();
        readGPS(gpsPort);
        gpsPort.clear();
        // wait 12 counts in order for the Geiger to stabilize
        writeLog();
      } else {
        postStatusMessageln("*** Null reading form Geiger.");
        gpsPort.clear();
      }
    }
    displayInfo();
  }
}

// function reads the gps data and sends it to the parsing function
void readGPS(Serial gpsPort) {
  int gotPosition = 0; 
  while (gotPosition == 0)
  {
    while (gpsPort.available () <= 0);
    String data = gpsPort.readStringUntil('\n');
    if ( data != null) {
      gotPosition = parseString(data);
    }
  }
}

//parsing function
int parseString (String serialString) {
  //sets up an array and splits the gps info by comma
  String items[] = split(serialString, ',');
  if (items[0].equals("$GPGGA") && items.length >= 9) {  //looks for the GPGGA info
    getGGA(items);
    return 1;
  } else {
    return 0;
  }
}
//this function assigns the variables
//to array elements that have been parsed
void getGGA(String[] gpsinfo) {
  time = gpsinfo[1];
  float LatDeg = floor(float(gpsinfo[2])/100.0);
  float LatMin = float(gpsinfo[2]) - LatDeg*100.0;
  Latitude1 = LatDeg + LatMin/60.0;
  Latitude = gpsinfo[2];
  NorSindicator = gpsinfo[3];
  float LonDeg = floor(float(gpsinfo[4])/100.0);
  float LonMin = float(gpsinfo[4]) - LonDeg*100.0;
  Longitude1 = LonDeg + LonMin/60.0;
  Longitude = gpsinfo[4];
  EorWindicator = gpsinfo[5];
  Quality = int(gpsinfo[6]);
  Satellites = gpsinfo[7];
  Precision = gpsinfo[8];
  Altitude = gpsinfo[9];
  gpsdata = join(gpsinfo, ",");
}
